/* 
 * File:   trackerFuncioonal.cpp
 * Author: emi
 *
 * Created on 15 de septiembre de 2013, 08:32 PM
 */
/////////////////
// Importaciones
/////////////////
#include <cstdlib>

#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>

//incluidos
#include <arpa/inet.h>  //inet_addr
#include <unistd.h> //close
#include <ctype.h> //touper


//poll
#include <poll.h>

using namespace std;


#define MAX_MSG_SIZE 1024
#define POLL_TIME 3500
#define DEBUG 1


/////////////////
// Definicion de tipos y listas
/////////////////

typedef struct cliente { //lista de clientes
    int socket; //socket de la coneccion establecida
    int ip; //de escucha 
    int puerto; // de escucha
    std::list<char*> archivosCompartidos; //lista de archivos compartidos
} Cliente;

// id va a el numero de socket
std::map<int, Cliente> mapClientes; // map<idCliente, cliente>


//estructura para manejar los archivo

typedef struct archivo {
    std::list<int> idCliente; //puede ser mas de un cliente, 
    //FIXME una lista de idCliente en vez de un int
    char* MD5;
    //char* nombreArchivo;
} Archivo;

// lista de arvhicos, con calve MD5
std::map<char*, Archivo> mapArchivos; // map<md5, archivo>

//lista de MD5 con clave nombre de archivo
std::map<char*, char*> mapMD5; // map<nombreArchivo, md5>


/////////////////
// Vaiables globales
/////////////////

int trackerSocket;
int puertoTracker;
char* ipTracker = "127.0.0.1";

int MAX_CLIENTES = 10;

struct pollfd pollSockets[MAX_CLIENTES]; //todos los clientes + el socket tracker
int cantSockets = 0;
int pollInt;

void inicializoPoll();
void crearSockeTracker();
void agregarCliente();
void atenderSolicitudes();

int main(int argc, char** argv) {

    inicializoPoll();
    crearSockeTracker();

    pollInt = poll(pollSockets, cantSockets, -1);

    if (pollInt == -1) {
        //Error al realizar poll
        printf("error occurred in poll()\n"); // error occurred in poll()
    } else if (pollInt == 0) {
        //Realizo controles
        // por ejemplo si se me desconecto algun cliente
        printf("Timeout occurred!  No data after %f seconds.\n", POLL_TIME);
    } else {
        agregarCliente();
        atenderSolicitudes();
    }

    while (1) {

    }

    return 0;
}

/////////////////
// Funciones aux
/////////////////

void inicializoPoll() {

    memset(pollSockets, 0, sizeof (pollSockets));
    //    for (int i = 1; i <= MAX_CLIENTES + 1; i++) {
    //        pollSockets[i].fd = -1;
    //    }
};

void crearSockeTracker() {
    //primitiva SOCKET
    trackerSocket = socket(AF_INET, SOCK_STREAM, 0);

    //primitiva BIND
    struct sockaddr_in server_addr;
    socklen_t server_addr_size = sizeof server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(puertoTracker);
    server_addr.sin_addr.s_addr = inet_addr(ipTracker);

    if (bind(trackerSocket, (struct sockaddr*) &server_addr, server_addr_size) < 0) return -1;
    // printf("Socket Servidor %d\n", bind(trackerSocket, (struct sockaddr*) &server_addr, server_addr_size));

    //primitiva LISTEN
    if (listen(trackerSocket, MAX_CLIENTES)) return -1;
    //printf("Socket Servidor %d\n", listen(trackerSocket, MAX_CLIENTES));

    //lo agrego a poll
    pollSockets[0].fd = trackerSocket;
    pollSockets[0].events = POLLIN; // | POLLPRI; // check for normal or out-of-band

    cantSockets++;

};

void agregarCliente() {
    // socket 0 es para nuevos clientes
    if (pollSockets[0].revents & POLLIN) {

        //primitiva ACCEPT
        struct sockaddr_in client_addr;
        socklen_t client_addr_size = sizeof client_addr;
        int socket_to_client = accept(trackerSocket, (struct sockaddr *) &client_addr, &client_addr_size);

        printf("Tu ip es: %s\n", inet_ntoa(client_addr.sin_addr));

        // guardo el socket en la lista de sockets del poll
        //primero busco un espacio vacio en el array
        int i = 1;
        while ((pollSockets[i].fd != -1) && (i <= MAX_CLIENTES + 1)) {
            i++;
        }
        if (i == MAX_CLIENTES + 1) {
            printf("Se ha alcanzado el numero maximo de clientes\n");
        } else {
            //agrego el soc//listaClientes[socket_to_client].archivosCompartidos = new std::list(); //creo lista vaciaket al array 
            pollSockets[i].fd = socket_to_client;
            pollSockets[i].events = POLLIN;

            cantSockets++;

            // el id del cliente es el file descriptor de su socket
            Cliente cli;
            cli.socket = socket_to_client;
            mapClientes[socket_to_client] = cli;

            //listaClientes[socket_to_client].archivosCompartidos = new std::list(); //creo lista vacia
        }
    }
}

void atenderSolicitudes() {
    int contSoc = 1;
    int atiendoSoc = cantSockets - 1; //quito el socket del tracker, estos son solo los de clientes

    while (atiendoSoc > 0) {
        if (pollSockets[contSoc].revents & POLLHUP) {
            atiendoSoc--; // atiendo el socket
            cantSockets--; //lo saco de la llista de disponibles
            contSoc++; //paso al siguiente
        } else
            if (pollSockets[contSoc].revents & POLLIN) {
            atiendoSoc--;

            pollSockets[contSoc].revents = 0;
            //un cliente envio un mensaje

            //primitiva RECEIVE
            char* data = (char*) malloc(MAX_MSG_SIZE);
            int data_size = MAX_MSG_SIZE;
            int received_data_size = recv(pollSockets[contSoc].fd, data, data_size, 0);
            printf("Recibido del cliente (%d bytes): %s\n", received_data_size, data);

            int i;
            for (i = 0; i < received_data_size; i++) {
                data[i] = toupper(data[i]);
            }
            //primitiva SEND
            int sent_data_size = send(pollSockets[contSoc].fd, data, received_data_size, 0);
            printf("Enviado al cliente (%d bytes): %s\n", sent_data_size, data);
        }
        contSoc++;
    }
}



